/*
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.dspl.validate.validator;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.dspl.model.Concept;
import com.google.dspl.model.Concept.ConceptProperty;
import com.google.dspl.model.DataType;
import com.google.dspl.model.Identifier;
import com.google.dspl.validate.DatasetResolver;
import com.google.dspl.validate.ItemReference;
import com.google.dspl.validate.ValidationContext;
import com.google.dspl.validate.violation.ConceptPropertyConceptNotFoundViolation;
import com.google.dspl.validate.violation.ConceptPropertyConflictingConceptTypeViolation;
import com.google.dspl.validate.violation.ConceptPropertyNoIdViolation;
import com.google.dspl.validate.violation.ConceptPropertyNoTypeViolation;
import com.google.dspl.validate.violation.Violation;

import java.util.List;

/**
 * {@link Validator} for {@link ConceptProperty}s.
 *
 * @author James Grimm
 */
class ConceptPropertyValidator implements Validator<ConceptProperty> {

  private final Concept concept;

  ConceptPropertyValidator(Concept concept) {
    this.concept = concept;
  }

  @Override
  public List<Violation> validate(ConceptProperty property, ValidationContext context) {
    ImmutableList.Builder<Violation> violations = ImmutableList.builder();
    validateId(property, context, violations);
    validateType(property, context, violations);
    validateConceptReference(property, context, violations);
    return violations.build();
  }

  private void validateConceptReference(ConceptProperty property, ValidationContext context,
      ImmutableList.Builder<Violation> violations) {
    Identifier conceptId = property.getConcept();
    if (conceptId != null) {
      DatasetResolver datasetResolver = context.getDatasetResolver();
      DataType dataType = property.getType();
      if (!ValidationHelper.isValidConceptReference(conceptId, context)) {
        violations.add(new ConceptPropertyConceptNotFoundViolation(
            propertyReference(property, context), property.getId(), concept.getId(), conceptId));
      } else {
        Concept propertyConcept = datasetResolver.getConcept(conceptId);

        // Check type matches
        DataType conceptType =
            datasetResolver.resolveConceptType(conceptId.getDatasetId(), propertyConcept);
        if (dataType == null) {
          dataType = conceptType;
        } else if (conceptType != null && dataType != conceptType) {
          violations.add(new ConceptPropertyConflictingConceptTypeViolation(
              propertyReference(property, context),
              property.getId(),
              concept.getId(),
              dataType,
              conceptId,
              conceptType));
        }
      }
    }
  }

  private void validateType(ConceptProperty property, ValidationContext context,
      ImmutableList.Builder<Violation> violations) {
    if (property.getType() == null && property.getConcept() == null) {
      violations.add(
          new ConceptPropertyNoTypeViolation(
              propertyReference(property, context),  property.getId(), concept.getId()));
    }
  }

  private void validateId(ConceptProperty property, ValidationContext context,
      ImmutableList.Builder<Violation> violations) {
    ItemReference propertyReference = propertyReference(property, context);
    if (Strings.isNullOrEmpty(property.getId()) && property.getConcept() == null) {
      violations.add(
          new ConceptPropertyNoIdViolation(propertyReference, concept.getId()));
    } else if (property.getId() != null) {
      IdValidator validator = new IdValidator(propertyReference);
      validator.validate(property.getId(), violations);
    }
  }

  private ItemReference propertyReference(ConceptProperty property, ValidationContext context) {
    return ItemReference.reference(context.getCurrentDataset(), concept, property);
  }
}
